package com.sftz.framework.mvc.processor;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.ActionContext;
import org.nutz.mvc.impl.processor.AbstractProcessor;

import com.uxuexi.core.common.util.Util;

/**
 * 安全性拦截器，拦截常见攻击如：CSRF漏洞 配合前端AJAX
 * SETUP使用，在发送AJAX请求时寻找Hidden表单token值存入header
 */
public class CSRFProtectProcessor extends AbstractProcessor {

	private static final String CSRF_TOKEN_NAME = CSRFTokenManager.CSRF_HEADER_NAME;

	Log log = Logs.get();

	@Override
	public void process(ActionContext ac) throws Throwable {
		HttpServletRequest req = ac.getRequest();
		HttpServletResponse resp = ac.getResponse();

		HttpSession session = req.getSession();

		// 从 session 中得到 csrftoken 属性
		String sToken = (String) session.getAttribute(CSRFTokenManager.CSRF_TOKEN_FOR_SESSION_ATTR_NAME);
		log.debug("csrftoken from session:" + sToken);
		if (Util.isEmpty(sToken)) {
			// 产生新的 token 放入 session 中
			//			sToken = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS);
			sToken = CSRFTokenManager.getTokenForSession(session);

			//存入cookie
			Cookie cookie = new Cookie(CSRF_TOKEN_NAME, sToken);

			//过期时间默认为浏览器会话期间(Session)，只要关闭浏览器窗口，cookie就消失
			//			cookie.setMaxAge(14 * 24 * 3600);

			//只有当前站点可以获取
			cookie.setPath("/");

			//限制脚本读取(js不能获取)
			cookie.setHttpOnly(true);

			//限制cookie必须使用https传输
			//			cookie.setSecure(true);

			resp.addCookie(cookie);
			req.setAttribute(CSRF_TOKEN_NAME, sToken);

			// 前后端分离的也从header带回去
			resp.addHeader(CSRF_TOKEN_NAME, sToken);

			doNext(ac);
			return;
		} else {

			/**
			 * 1,验证参数中的csrftoken或者HTTP头中取得的csrftoken 
			 * 任意一个满足即可
			 */
			// 从 HTTP 头中取得 csrftoken 
			String xhrToken = req.getHeader(CSRFTokenManager.CSRF_HEADER_NAME);

			// 从请求参数中取得 csrftoken(GET/POST) 
			String pToken = req.getParameter(CSRF_TOKEN_NAME);

			if (null != sToken && sToken.equals(xhrToken)) {
				doNext(ac);
				return;
			} else if (null != sToken && sToken.equals(pToken)) {
				doNext(ac);
				return;
			} else {

				//2,从cookie获取  TODO 最好是将token写入html文档，发请求的时候带上
				Cookie[] cookies = req.getCookies();
				if (null != cookies && cookies.length > 0) {
					for (Cookie cookie : cookies) {
						// get the cookie name
						String cookName = cookie.getName();
						if (Util.eq(CSRF_TOKEN_NAME, cookName)) {
							// get csrftoken from cookie
							String cookieToken = cookie.getValue();
							log.debug("csrftoken from cookie:" + cookieToken);
							if (Util.eq(sToken, cookieToken)) {
								doNext(ac);
								return;
							}
						}
					}
				}

				//返回403
				log.warn("Invalid request header,could not find a valid CSRF_TOKEN!");
				ac.getResponse().sendError(HttpServletResponse.SC_FORBIDDEN,
						"Invalid request header,could not find a valid CSRF_TOKEN!");
			}
		}

	}

}
